package hust.rpc.proxy;

import hust.rpc.constants.Constants;
import hust.rpc.enums.RpcResponseType;
import hust.rpc.invoker.RemoteInvoker;
import hust.rpc.pojo.Message;
import hust.rpc.pojo.RpcRequest;
import hust.rpc.pojo.RpcResponse;
import hust.rpc.serialize.json.JsonUtils;
import hust.rpc.utils.SignatureUtils;
import hust.rpc.utils.SingletonUtils;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@Slf4j
public class ConsumerProxy extends hust.rpc.proxy.Proxy {

    // 方法签名缓存
    private final Map<Method, String> cache = new ConcurrentHashMap<>();

    // id生成器
    private final AtomicLong idGenerator = new AtomicLong(0);

    /**
     * 获取服务消费接口代理类
     *
     * @param clazz 接口Class
     * @param <T>   接口类型
     * @return 接口代理对象
     */
    @Override
    @SuppressWarnings("unchecked")
    protected <T> T getInternalProxy(Class<T> clazz) {
        // 通过Jdk动态代理创建代理对象
        Object invoker = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{clazz}, (proxy, method, args) -> {
            if (!cache.containsKey(method)) {
                cache.put(method, SignatureUtils.getSignature(method));
            }
            RpcRequest request = RpcRequest.builder()
                    .args(args)
                    .id(idGenerator.getAndIncrement())
                    .signature(cache.get(method))
                    .build();
            Message message = Message.builder()
                    .codec(Constants.CODEC_KRYO)
                    .version(Constants.VERSION)
                    .messageType(Constants.REQUEST_TYPE)
                    .data(request)
                    .build();
            RpcResponse response = SingletonUtils.getInstance(RemoteInvoker.class).sendRequest(message).get();
            if (response.getResponseType() == RpcResponseType.SUCCESS) {
                return response.getResult();
            }
            throw new RuntimeException(String.valueOf(response.getResult()));
        });
        return (T) invoker;
    }
}
